'''
Given one set of parameters, run tests to fill one graph.

'''

import RTCSA_TaskSystem2 as taskSystem
import numpy as np
from random import *



#NUM_METHODS=2

#SPLIT_UNIFORM=taskSystem.SPLIT_UNIFORM
#SPLIT_NORMAL=taskSystem.SPLIT_NORMAL


'''
this is a guess.  Don't know what's really the best thing here.
Probably more efficient to maximize graph parallelism, but I do have a strict 
time limit.
0 is the default, i.e. let Gurobi figure it out
'''
THREADS_PER_TEST=0


#pretty_print = {BASELINE: "baseline", SMT_ONE_FRAME: "SMT with One Frame Size", CERT_MT: "SMT with Multiple Frame Sizes", FULL_P_CERT_MT: "SMT with Full Premption"}

#myTaskSet = taskSystem.RTCSA_TaskSystem2(minTotalUtil, minTaskUtil, maxTaskUtil,
#                                         expected_score, slope, prob_harm, degree_harm, score_distro,
#                                         coreCount, periodMin, periodCount, timeout, solutionLimit, lowerBound,
#                                         upperBound, threadsPerTest)

def runParamTest(minTotalUtil, maxTotalUtil, bin_size, min_samples, minTaskUtil, maxTaskUtil,
                 expected_score, slope, prob_harm, degree_harm, score_distro,
                 coreCount, periodMin, periodCount, timeout, solutionLimit, lowerBound, upperBound):
    threadsPerTest=THREADS_PER_TEST

    test_range = maxTotalUtil - minTotalUtil
    numBins = int(np.ceil(test_range/bin_size))
    # "theBins": index 0 is number of task sets in bin, following indicies are how many of those are schedulable for various algorithms
    theBins = []
    for i in range(0, numBins):
        theBins.append(0)
        #one bin for each SMT method, plus one for baseline
        resultBins=1+taskSystem.NUM_INNER_COST_MODELS*taskSystem.NUM_PARTITION_ALGS
        theBins[i]=[0]*(resultBins+1)
        #theBins.append([0] * NUM_METHODS +1)

    # Iterate until we've tried at least 'min_samples' task sets at our base utilization
    coreCountBin=int(np.floor((coreCount-minTotalUtil)/bin_size))
    while theBins[coreCountBin][0] < min_samples:
        #create task set
        #def __init__(self, setUtil, utilMin, utilMax, expected_score, slope, prob_harm, degree_harm, score_distro,
        #             core_count,
        #             periodMin, periodCount, timeout, solutionLimit, lowerBound, upperBound, threadsPerTest):

        myTaskSet = taskSystem.TaskSet(minTotalUtil, minTaskUtil, maxTaskUtil,
                                                 expected_score, slope, prob_harm, degree_harm, score_distro,
                                                 coreCount, periodMin, periodCount, timeout, solutionLimit, lowerBound,
                                                 upperBound, threadsPerTest)
        # Loop until we reach a task set that nothing can schedule
        runtime=0
        while True:
            # Depending on where our utilization fell as we added tasks, we choose a bin
            #this is the starting bin
            myBin = int(np.floor((myTaskSet.totalUtil - minTotalUtil) / bin_size))
            #break if we overshoot desired range
            if len(theBins)-1 < myBin:
                break #out of while TRUE
            
            #not on server as of 3/28 6:39 pm
            #version on server ends up returning the last runtime
            #this will give the max runtime per scenario.
            (result, newRuntime)=myTaskSet.testSystem()
            runtime=max(runtime, newRuntime)
            #note a trial took place in size range myBin
            theBins[myBin][0]+=1
            
            #update all entries in myBin
            allFalse=True
            if result[0]==True:
                #add a success for baseline
                theBins[myBin][1]+=1
                allFalse=False
            first_smt_bin=2
            for alg in range(taskSystem.NUM_PARTITION_ALGS):
                for model in range(taskSystem.NUM_INNER_COST_MODELS):
                    #relativeBin=0 gives the first result for SMT testing
                    relativeBin=alg*taskSystem.NUM_INNER_COST_MODELS+model
                    if result[1][alg][model]:
                        theBins[myBin][first_smt_bin+relativeBin]+=1
                        allFalse=False   
            if allFalse:
                break #out of while True; we didn't have any success
            else:
                myTaskSet.addTask()

        # If we reach a task set that's unschedulable no matter what, assume it's unschedulable for higher utilizations.
        # (We could just simulate all the higher-utilization bins, but that's slow.)
        totalUtil = myTaskSet.totalUtil + uniform(minTaskUtil, maxTaskUtil)
        while totalUtil < maxTotalUtil:
            myBin=int(np.floor((totalUtil-minTotalUtil) /bin_size))
            #myBin = int(np.floor((myTaskSet.totalUtil - minTotalUtil) / bin_size))
            theBins[myBin][0]+=1
            totalUtil = totalUtil +uniform(minTaskUtil, maxTaskUtil)
        #print("utilMin, utilMax, symMean, symDev", minTaskUtil, maxTaskUtil, symMean, symDev)
        #print("theBins[0][0]=",theBins[0][0])
        #print("theBins=", theBins)
    #go back and start a new task set
    #print(theBins)
    return(theBins, runtime)

def main():
    #parameterrs for testing
    minTotalUtil=3
    maxTotalUtil=5
    bin_size=0.25
    min_samples=5
    minTaskUtil=.3
    maxTaskUtil=.7
    #need to prevents costs<1
    periodSet=[20, 40, 60, 80, 100]
    symDistrib=SPLIT_UNIFORM
    symParam1=.1
    symParam2=.8
    symParam3=.1
    coreCount=4
    timeout=30
    
    
    runParamTest(minTotalUtil, maxTotalUtil, bin_size, min_samples, minTaskUtil, maxTaskUtil, periodSet, symParam1, symParam2, symParam3, symDistrib, coreCount, timeout)

if __name__== "__main__":
     main() 



#test=runParamTest(minTotalUtil, maxTotalUtil, binSize, minSamples, minTaskUtil, maxTaskUtil, periodMin, periodCount, symMean, symDev, symDistrib, coreCount, timeout)
